#include <assert.h>
#include <imbus.h>
#include <stdio.h>
#include <test_override.h>

static IMConstPointer __one_parent_class   = 0;
static IMConstPointer __two_parent_class   = 0;
static IMConstPointer __three_parent_class = 0;
static IMConstPointer __four_parent_class  = 0;

static void
__one_ctor (IMPointer obj)
{
    fprintf (stderr, "One::ctor(%p)\n", obj);

    assert (IS_ONE(obj));
}

static void
__one_dtor (IMPointer obj)
{
    fprintf (stderr, "One::dtor(%p)\n", obj);

    assert (IS_ONE(obj));
}

static void
__one_copy (IMPointer dest, IMConstPointer src)
{
    One *dp = ONE (dest);
    const One *sp = CONST_ONE (src);

    assert (dp && sp);

    IM_OBJECT_CLASS(__one_parent_class)->copy (dest, src);

    fprintf (stderr, "One::copy(%p, %p)\n", dest, src);
}

static void
__one_foo (IMPointer obj)
{
    assert (IS_ONE(obj));

    fprintf (stderr, "One::foo(%p)\n", obj);
}

static void
__one_class_init (IMPointer klass)
{
    ((IMObjectClass *)klass)->copy  = __one_copy;

    ((OneClass*)klass)->foo         = __one_foo;

    __one_parent_class              = im_object_class_get_parent (klass);
}

IMType
one_get_type ()
{
    static IMTypeInfo one_info =
    {
        sizeof (OneClass),
        __one_class_init,
        0,
        sizeof (One),
        __one_ctor,
        __one_dtor
    };

    static IMType __one_type = 0;

    if (!__one_type) {
        __one_type = im_type_register_class (IM_TYPE_OBJECT,
                                             "One",
                                             &one_info,
                                             TRUE);
    }

    return __one_type;
}

static void
__two_ctor (IMPointer obj)
{
    fprintf (stderr, "Two::ctor(%p)\n", obj);

    assert (IS_TWO(obj));
}

static void
__two_dtor (IMPointer obj)
{
    fprintf (stderr, "Two::dtor(%p)\n", obj);

    assert (IS_TWO(obj));
}

static void
__two_copy (IMPointer dest, IMConstPointer src)
{
    Two *dp = TWO (dest);
    const Two *sp = CONST_TWO (src);

    assert (dp && sp);

    IM_OBJECT_CLASS(__two_parent_class)->copy (dest, src);

    fprintf (stderr, "Two::copy(%p, %p)\n", dest, src);
}

static void
__two_class_init (IMPointer klass)
{
    ((IMObjectClass *)klass)->copy  = __two_copy;

    __two_parent_class              = im_object_class_get_parent (klass);
}

IMType
two_get_type ()
{
    static IMTypeInfo two_info =
    {
        sizeof (TwoClass),
        __two_class_init,
        0,
        sizeof (Two),
        __two_ctor,
        __two_dtor
    };

    static IMType __two_type = 0;

    if (!__two_type) {
        __two_type = im_type_register_class (TYPE_ONE,
                                             "Two",
                                             &two_info,
                                             TRUE);
    }

    return __two_type;
}

static void
__three_ctor (IMPointer obj)
{
    fprintf (stderr, "Three::ctor(%p)\n", obj);

    assert (IS_THREE(obj));
}

static void
__three_dtor (IMPointer obj)
{
    fprintf (stderr, "Three::dtor(%p)\n", obj);

    assert (IS_THREE(obj));
}

static void
__three_copy (IMPointer dest, IMConstPointer src)
{
    Three *dp = THREE (dest);
    const Three *sp = CONST_THREE (src);

    assert (dp && sp);

    IM_OBJECT_CLASS(__three_parent_class)->copy (dest, src);

    fprintf (stderr, "Three::copy(%p, %p)\n", dest, src);
}

static void
__three_foo (IMPointer obj)
{
    fprintf (stderr, "Three::foo(%p)\n", obj);

    assert (IS_THREE (obj));

    ONE_CLASS(__three_parent_class)->foo (obj);
}

static void
__three_bar (IMPointer obj)
{
    fprintf (stderr, "Three::bar(%p)\n", obj);

    assert (IS_THREE (obj));
}

static void
__three_class_init (IMPointer klass)
{
    ((IMObjectClass *)klass)->copy  = __three_copy;

    ((OneClass*)klass)->foo           = __three_foo;

    ((ThreeClass*)klass)->bar         = __three_bar;

    __three_parent_class            = im_object_class_get_parent (klass);
}

IMType
three_get_type ()
{
    static IMTypeInfo three_info =
    {
        sizeof (ThreeClass),
        __three_class_init,
        0,
        sizeof (Three),
        __three_ctor,
        __three_dtor
    };

    static IMType __three_type = 0;

    if (!__three_type) {
        __three_type = im_type_register_class (TYPE_TWO,
                                             "Three",
                                             &three_info,
                                             TRUE);
    }

    return __three_type;
}

static void
__four_ctor (IMPointer obj)
{
    fprintf (stderr, "Four::ctor(%p)\n", obj);

    assert (IS_FOUR(obj));
}

static void
__four_dtor (IMPointer obj)
{
    fprintf (stderr, "Four::dtor(%p)\n", obj);

    assert (IS_FOUR(obj));
}

static void
__four_copy (IMPointer dest, IMConstPointer src)
{
    Four *dp = FOUR (dest);
    const Four *sp = CONST_FOUR (src);

    assert (dp && sp);

    IM_OBJECT_CLASS(__four_parent_class)->copy (dest, src);

    fprintf (stderr, "Four::copy(%p, %p)\n", dest, src);
}

static void
__four_foo (IMPointer obj)
{
    fprintf (stderr, "Four::foo(%p)\n", obj);

    assert (IS_FOUR(obj));

    ONE_CLASS(__four_parent_class)->foo (obj);
}

static void
__four_bar (IMPointer obj)
{
    fprintf (stderr, "Four::bar(%p)\n", obj);

    assert (IS_FOUR(obj));

    THREE_CLASS(__four_parent_class)->bar (obj);
}

static void
__four_class_init (IMPointer klass)
{
    ((IMObjectClass *)klass)->copy  = __four_copy;

    ((OneClass*)klass)->foo           = __four_foo;
    ((ThreeClass*)klass)->bar         = __four_bar;

    __four_parent_class              = im_object_class_get_parent (klass);
}

IMType
four_get_type ()
{
    static IMTypeInfo four_info =
    {
        sizeof (FourClass),
        __four_class_init,
        0,
        sizeof (Four),
        __four_ctor,
        __four_dtor
    };
    static IMType __four_type = 0;

    if (!__four_type) {
        __four_type = im_type_register_class (TYPE_THREE,
                                             "Four",
                                             &four_info,
                                             TRUE);
    }

    return __four_type;
}

One*
one_new ()
{
    return (One*) im_object_new (TYPE_ONE);
}
void
one_foo (IMPointer obj)
{
    const OneClass *cp = ONE_GET_CLASS (obj);

    assert (cp);

    cp->foo (obj);
}

Two*
two_new ()
{
    return (Two*) im_object_new (TYPE_TWO);
}

Three*
three_new ()
{
    return (Three*) im_object_new (TYPE_THREE);
}

void
three_bar (IMPointer obj)
{
    const ThreeClass *cp = THREE_GET_CLASS (obj);

    assert (cp);

    cp->bar (obj);
}

Four*
four_new ()
{
    return (Four*) im_object_new (TYPE_FOUR);
}

int main (int argc, char *argv [])
{
    IMPointer obj;
    IMPointer obj2;

    im_init (&argc, &argv);

    obj = one_new ();
    one_foo (obj);
    im_object_unref (obj);

    obj = two_new ();
    one_foo (obj);
    im_object_unref (obj);

    obj = three_new ();
    one_foo (obj);
    three_bar (obj);
    im_object_unref (obj);

    obj = four_new ();
    one_foo (obj);
    three_bar (obj);
    im_object_unref (obj);

    obj  = two_new ();
    obj2 = four_new ();

    im_object_copy (obj, obj2);

    /* Will fail 
    im_object_copy (obj2, obj);
    */

    im_object_unref (obj);
    im_object_unref (obj2);
    return 0;
}
/*
vi:ts=4:nowrap:ai:expandtab
*/
